/*
 *  Copyright (C) 2015 The yuhaiyang Android Source Project
 *
 *  Licensed under the yuhaiyang License, (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       https://coding.net/yuhaiyang/License.git
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  @author: y.haiyang@qq.com
 *
 */

package com.yuhaiyang.xmlparser;

import android.util.Log;

import com.yuhaiyang.utils.Utils;

import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;


public class SaxBookParser implements IBookParser {
    private static final String TAG = "SaxBookParser";

    @Override
    public List<Book> parse(InputStream is) throws Exception {
        /**
         * 主要流程：
         *   1. 创建工厂对象
         *   2. 通过工厂来生成解析器
         *   3. 实例化   DefaultHandler（解析内容都会输出到Handle内）
         *   4. 开始解析
         */
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        SaxParserHandler handler = new SaxParserHandler();
        parser.parse(is, handler);
        return handler.getBooks();
    }

    /**
     * 创建一个XML
     */
    @Override
    public String write(List<Book> books) throws Exception {
        /**
         * 主要流程：
         *      1. 创建工厂对象
         *      2. 通过工厂生成 对象
         *      3. 设定相关参数
         *      4. 循环添加
         */
        SAXTransformerFactory factory = (SAXTransformerFactory) TransformerFactory.newInstance();
        TransformerHandler handler = factory.newTransformerHandler();

        // Transformer 设定参数 编码方式等
        Transformer transformer = handler.getTransformer();
        // 设置输出采用的编码方式
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        // 是否自动添加额外的空白
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        // 是否忽略XML声明
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");

        StringWriter writer = new StringWriter();
        Result result = new StreamResult(writer);
        handler.setResult(result);

        //代表命名空间的URI 当URI无值时 须置为空字符串
        String uri = "";
        //命名空间的本地名称(不包含前缀) 当没有进行命名空间处理时 须置为空字符串
        String localName = "";
        // 缩进
        String tab = "\n    ";
        String two_tab = "\n        ";
        char[] ch = null;
        handler.startDocument();
        // 这里需要考虑写入换行
        ch = Utils.NEW_LINE.toCharArray();
        handler.characters(ch, 0, ch.length);

        // 写入books标签
        handler.startElement(uri, localName, "books", null);
        //负责存放元素的属性信息
        AttributesImpl attrs = new AttributesImpl();

        /**
         * 循环写入每一个标签
         * 写入流程：
         *    1. startElement
         *    2. characters
         *    3. endElement
         *  和解析的流程是一样的！
         */
        for (Book book : books) {
            //清空属性列表
            attrs.clear();
            //添加一个名为id的属性(type影响不大,这里设为string
            // 例如： <book id="1001">
            //attrs.addAttribute(uri, localName, "id", "string", String.valueOf(book.getId()));

            // 插入换行
            ch = tab.toCharArray();
            handler.characters(ch, 0, ch.length);

            //开始一个book元素 关联上面设定的id属性
            handler.startElement(uri, localName, "book", attrs);

            //开始一个name元素 没有属性
            ch = two_tab.toCharArray();
            handler.characters(ch, 0, ch.length);

            handler.startElement(uri, localName, "name", null);
            ch = String.valueOf(book.getName()).toCharArray();
            handler.characters(ch, 0, ch.length);
            handler.endElement(uri, localName, "name");

            // 开始一个id元素
            ch = two_tab.toCharArray();
            handler.characters(ch, 0, ch.length);

            handler.startElement(uri, localName, "id", null);
            ch = String.valueOf(book.getId()).toCharArray();
            handler.characters(ch, 0, ch.length);
            handler.endElement(uri, localName, "id");

            //开始一个price元素 没有属性
            ch = two_tab.toCharArray();
            handler.characters(ch, 0, ch.length);

            handler.startElement(uri, localName, "price", null);
            ch = String.valueOf(book.getPrice()).toCharArray();
            handler.characters(ch, 0, ch.length);
            handler.endElement(uri, localName, "price");
            // book的结束
            ch = tab.toCharArray();
            handler.characters(ch, 0, ch.length);

            handler.endElement(uri, localName, "book");
        }

        handler.endElement(uri, localName, "books");
        handler.endDocument();

        return writer.toString();
    }

    /**
     * 方法调用流程：
     * 1. startDocument
     * 2. startElement 开始解析
     * 3. characters 当前行解析的结果
     * 4. endElement 只有遇到</*>才会调用到
     */
    private class SaxParserHandler extends DefaultHandler {

        private List<Book> mBooks;
        private Book mBook;
        private StringBuilder mStringBuilder;

        /**
         * @return Books
         */
        public List<Book> getBooks() {
            return mBooks;
        }

        // 开始解析 可以在这个里面初始化对象
        @Override
        public void startDocument() throws SAXException {
            Log.i(TAG, "===========startDocument==============");
            super.startDocument();
            mBooks = new ArrayList<Book>();
            mStringBuilder = new StringBuilder();
        }

        // 开始解析标签

        /**
         * localName 和 name 对应的是 标签的名字，
         * attr里面包含的是属性 例如: <book id="1001"> id就是属性
         */
        @Override
        public void startElement(String uri, String localName, String name, Attributes attr) throws SAXException {
            super.startElement(uri, localName, name, attr);
            Log.i(TAG, "startElement localName: " + localName + ", name: " + name);
            if (localName.equals("book")) {
                mBook = new Book();
            }
            //将字符长度设置为0 以便重新开始读取元素内的字符节点
            mStringBuilder.setLength(0);
        }

        // 当前标签解析出来的内容

        /**
         * ch 是当前标签解析出来的内容， 可以赋值了.
         */
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            super.characters(ch, start, length);

            //将读取的字符数组追加到builder中
            mStringBuilder.append(ch, start, length);
            Log.i(TAG, "characters start = " + start + " , length = " + length);
            Log.i(TAG, "characters mStringBuilder = " + mStringBuilder.toString());
        }

        // 当遇到</*>时候解析当前标签完毕
        @Override
        public void endElement(String uri, String localName, String name) throws SAXException {
            super.endElement(uri, localName, name);
            Log.i(TAG, "endElement  localName = " + localName + "  ,name = " + name);
            Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++");
            if (localName.equals("id")) {
                mBook.setId(Integer.parseInt(mStringBuilder.toString()));
            } else if (localName.equals("name")) {
                mBook.setName(mStringBuilder.toString());
            } else if (localName.equals("price")) {
                mBook.setPrice(Float.parseFloat(mStringBuilder.toString()));
            } else if (localName.equals("book")) {
                mBooks.add(mBook);
            }
        }
    }
}
